/**************************************************************************
*   Copyright (C) 2009 Chernov Dmitry                                     *
*   diman4ik.chernov@gmail.com                                            *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#include "korners_game_engine_ai.h"

#include <QPoint>
#include <KDebug>


const char KornersGameEngineAI::static_values[] = { 0,  1,  1,  2,  2,  2,  2,  2,
                                                    1,  1,  2,  3,  3,  3,  3,  3,
                                                    1,  2,  3,  4,  4,  4,  4,  4,
                                                    2,  3,  6,  6,  6,  6,  5,  5,
                                                    2,  3,  6,  8,  8,  8,  7,  7,
                                                    2,  3,  6,  8,  10, 11, 12, 13,
                                                    2,  3,  5,  7,  11, 12, 13, 14,
                                                    2,  3,  5,  7,  12, 13, 14, 14  };


KornersGameEngineAI::KornersGameEngineAI() 
{
    m_endSpiel = false;
}

int KornersGameEngineAI::getScores( std::multimap<int, int>& all,
                                    std::list<MoveInfo>& ret,
                                    owner tp )
{
    ret.clear();

    std::multimap<int, int>::iterator it;

    int value = 0;
    int n_good_moves = 0;

    QPoint src;
    QPoint dst;

    for( it = all.begin(); it != all.end(); ++it )
    {
	src = indexToPoint(it->first);
	dst = indexToPoint(it->second);

	n_good_moves++;

        if( tp == opponent )
	    value = static_values[ it->second ] - static_values[it->first];
	else
	    value = static_values[ rotateIndex( it->second ) ] - static_values[ rotateIndex( it->first ) ];
        
	ret.push_back( MoveInfo( src, dst, value ) );
    }

    ret.sort( std::greater<MoveInfo>( ) );
    return n_good_moves;
}

void KornersGameEngineAI::pointsToGo( QPoint from_where, QVector<int>& ret ) const
{
    bool reachable[ KornersEngine::FIELD_SIZE*KornersEngine::FIELD_SIZE ];
    memset( reachable, false, FIELD_SIZE*FIELD_SIZE );
    markReachable( from_where, none, reachable );

    for( int i = 0; i < FIELD_SIZE*FIELD_SIZE; i++ )
    {
	if( ( reachable[i] == true ) && ( m_field[i] == empty ) )
	    ret.push_back(i);
    }
}

int KornersGameEngineAI::scoreAvailableMoves( std::list<MoveInfo>& moves, owner tp)
{
    std::multimap<int, int> src_vs_dests;
    QVector<int> avail_moves;

    for( int i = 0; i < FIELD_SIZE*FIELD_SIZE; i++ )
    {
	if( m_field[i] == tp )
	{
	    QPoint src = indexToPoint(i);
	    avail_moves.clear();
	    pointsToGo( src, avail_moves );
	    for( int j = 0; j < avail_moves.size(); j++ )
		src_vs_dests.insert( std::make_pair<int, int>( i, avail_moves[j] ) );
	}
    }

    return getScores( src_vs_dests, moves, tp );
}

KornersGameEngineAI::MoveInfo KornersGameEngineAI::search( int depth )
{
    std::list<MoveInfo> moves;
    std::list<MoveInfo> tested_moves;

    m_curPositionVal = calcPosStaticValue( opponent );

    if( calcEnemyBaseCaps( opponent ) > 9 )
        m_endSpiel = true;

    int n_good_moves = scoreAvailableMoves( moves, opponent);

    ( n_good_moves > 20 ) ? n_good_moves = 20 : n_good_moves;

    std::list<MoveInfo>::iterator it;
    int i = 0;
    int diff = 0;

    kDebug() << moves.size();

    kDebug() << calcPosStaticValue(player) << ":" << calcPosStaticValue(opponent); 

    for( it = moves.begin() ; it != moves.end() ; i++, ++it )
    {
        if( i == n_good_moves )
            break;
        
        kDebug() << ( *it ).begin.x() << ( *it ).begin.y() << " : " << ( *it ).end.x() << ( *it ).end.y() ;

	applyMove( (*it).begin, (*it).end );

        if( innerSearch( &diff, depth - 1, player )  ) 
        {
            kDebug() << "ret == true";
	    applyMove( (*it).end, (*it).begin );
            return *it;
        }

        //// + moveValue( (*it).begin, (*it).end );
	correctValue( (*it).begin, (*it).end, diff );
        kDebug() <<  diff << " : " << groupingBonus();
        (*it).profit = diff + groupingBonus();
        diff = 0;
	tested_moves.push_back( *it );
	applyMove( (*it).end, (*it).begin );
    }

    tested_moves.sort( std::greater<MoveInfo>() );
    return ( tested_moves.front() );
}

bool KornersGameEngineAI::innerSearch( int* diff, int depth, owner tp )
{
    if( gameOver() == opponent )
    {
        *diff += 1000;
        return true;
    }

    bool ret = false;

    if( depth%2 == 0 )
    {	
	
        int playerVal = calcPosStaticValue(player);     
        int opponentVal = calcPosStaticValue(opponent);
	int this_diff = ( opponentVal - m_curPositionVal )*3 + ( opponentVal - playerVal )*2;
	*diff += this_diff;
            
        
        if( depth == 0 )
            return false; 
    }

    std::list<MoveInfo> moves;
    int n_good_moves = scoreAvailableMoves( moves, tp );

    ( n_good_moves > 12 ) ? n_good_moves = 12 : n_good_moves;

    std::list<MoveInfo>::iterator it;
    int i = 0;
    for( it = moves.begin() ; it != moves.end() ; i++, ++it )
    {
        if( i == n_good_moves )
                break;

	applyMove( (*it).begin, (*it).end );
        ret = innerSearch( diff, depth - 1, owner( tp%2 + 1 ) );
	applyMove( (*it).end, (*it).begin );
    }

    return ret;
}

void KornersGameEngineAI::correctValue(  QPoint src, QPoint , int& value )
{
    if( !m_endSpiel )
    {
	if( ( src.x() > FIELD_SIZE - IN_ROW ) &&
	    ( src.y() > FIELD_SIZE - N_ROWS ) )
	    value *= 0.8;
	
	if( ( src.x() < IN_ROW ) &&
	    ( src.y() < N_ROWS ) )
	    value *= 1.1;
    }
}

void KornersGameEngineAI::makeNextMove( const QPoint& src, const QPoint& dst )
{
    // Сохраним поле на всякий случай
    char field[ FIELD_SIZE*FIELD_SIZE ];
    memcpy( field, m_field, FIELD_SIZE*FIELD_SIZE );

    QPoint src_comp( FIELD_SIZE - src.x() - 1,
                     FIELD_SIZE - src.y() - 1 );
    QPoint dst_comp( FIELD_SIZE - dst.x() - 1,
                     FIELD_SIZE - dst.y() - 1 );
    MoveInfo ret = search(4);
    kDebug() << ret.begin.x() << " : " << ret.begin.y() << " ; "
             << ret.end.x() << " : " << ret.end.y() << " val = " << ret.profit;

    memcpy( m_field, field, FIELD_SIZE*FIELD_SIZE );

    computerMove( ret.begin, ret.end );
}

bool KornersGameEngineAI::canSelect( const QPoint& src ) const
{
    if( m_field[ pointToIndex(src) ] == player )
	return true;
    return false;
}

bool KornersGameEngineAI::canMove( QPoint src, QPoint dst ) const
{
    if( isMoveValid( src, dst, player ) )
	return true;
    return false;
}
